#!/usr/bin/python -B

#
# Dispatch for generating C code, similar to
# application or driver logging.
#

from string import Template, upper, replace
from copy   import deepcopy

from ApiCodeGen   import *
from ApiUtil      import outputCode
from ApiUtil      import typeIsVoid
from ApiType      import typeStrip
from ApiRegal     import logParameter
from ApiRegal     import cCodeParameter
from ApiRegal     import helperMap

from RegalContextInfo import cond

# Exclude some functions from the output

exclude = set(['glGetString','glGetIntegerv','glGetError','glGetGraphicsResetStatusARB', 'glGetProgramInfoLog'])

exclude.update(['glGetTexLevelParameteriv','glGetTexImage'])

# Code generation for dispatch table init.

def apiDispatchCodeInitCode(apis, args, dispatchName):

  code = ''

  for api in apis:
    if api.name=='gl':

      for function in api.functions:

        if not function.needsContext:
          continue

        if getattr(function,'regalOnly',False)==True:
          continue

        if function.name in exclude:
          continue

        name = function.name
        code += '  tbl.%s = %s_%s;\n' % ( name, dispatchName, name )

  return code

# Template for RegalDispatchCode.cpp

dispatchCodeTemplate = Template('''${AUTOGENERATED}
${LICENSE}

#include "pch.h" /* For MS precompiled header support */

#include "RegalUtil.h"

#if REGAL_CODE

REGAL_GLOBAL_BEGIN

#include <string>

#include <boost/print/string_list.hpp>

#include "RegalLog.h"
#include "RegalPush.h"
#include "RegalToken.h"
#include "RegalHelper.h"
#include "RegalContext.h"

using namespace ::REGAL_NAMESPACE_INTERNAL::Logging;
using namespace ::REGAL_NAMESPACE_INTERNAL::Token;

REGAL_GLOBAL_END

REGAL_NAMESPACE_BEGIN

using namespace ::boost::print;

${API_FUNC_DEFINE}

void InitDispatchTableCode(DispatchTableGL &tbl)
{
${API_GLOBAL_DISPATCH_INIT}
}

REGAL_NAMESPACE_END

#endif
''')

# Code generation for generating C code from GL API calls

def generateDispatchCode(apis, args):

  code = ''

  for api in apis:
    if api.name=='gl':

      for function in api.functions:

        if not function.needsContext:
          continue

        if getattr(function,'regalOnly',False)==True:
          continue

        if function.name in exclude:
          continue

        f = deepcopy(function)

        name = f.name
        params = paramsDefaultCode(f.parameters, True)

        callParams = paramsNameCode(f.parameters)
        rType  = typeCode(f.ret.type)

        code += 'static %sREGAL_CALL %s%s(%s) \n{\n' % (rType, 'code_', name, params)

        code += '    RegalContext *_context = REGAL_GET_CONTEXT();\n'
        code += '    RegalAssert(_context);\n'
        code += '    DispatchTableGL *_next = _context->dispatcher.code.next();\n'
        code += '    RegalAssert(_next);\n'
        code += '    '

        if not typeIsVoid(rType):
          code += '%s _ret = '%(rType)
        code += '_next->call(&_next->%s)(%s);\n' % ( name, callParams )

        # comment-out calls to functions flagged as trace = False

        prefix = ''
        suffix = ''
        if not f.trace:
          prefix += '/* '
          suffix += ' */'

        h      = False
        h1 =  '    std::string indent((_context->depthBeginEnd + _context->depthPushAttrib + 1)*2,\' \');\n'
        h2 =  ''
        body   =  ''

        ret = ''
        if not typeIsVoid(rType):
          if   f.name in [ 'glCreateShader','glCreateShaderObjectARB' ]:
            h2 += '    size_t _retIndex = _context->codeShaderNext++;\n'
            ret = 'const %s shader\" << _retIndex << \" = '%typeStrip(rType)
          elif f.name in [ 'glCreateProgram','glCreateProgramObjectARB']:
            h2 += '    size_t _retIndex = _context->codeProgramNext++;\n'
            ret = 'const %s program\" << _retIndex << \" = '%typeStrip(rType)
          else:
            h2 += '    size_t _retIndex = _context->codeOutputNext++;\n'
            ret = 'const %s o\" << _retIndex << \" = '%typeStrip(rType)

        if len(f.parameters)==0:
          body += '    %s_code << indent << "%s%s();%s\\n";\n'%(prefix,ret,f.name,suffix)
        else:
          body += '    %s_code << indent << "%s%s(";\n'%(prefix,ret,f.name)

          if f.name.startswith('glShaderSource'):
            h2 += '    std::string _delim = print_string("\\\\n\\"\\n",indent,"  \\"");\n'
            h2 += '    size_t _stringIndex = _context->codeInputNext++;\n'
            h2 += '    _code << indent << \"const char *i\" << _stringIndex << \" =\\n\";\n'
            h2 += '    _code << indent << "  \\\"" << string_list< ::std::string >(string_list< ::std::string >(count,string,length).str(),\'\\n\').join(_delim) << "\\";\\n";\n'
            body   += '    _code << %s << ",1,&i" <<_stringIndex << ",NULL);\\n";\n'%(f.parameters[0].name)

          else:
            delim = False
            for i in f.parameters:
              if delim:
                body += '    _code << ", "; '
              elif len(f.parameters)>1:
                body += '                   '
              else:
                body += '    '

#             p = cCodeParameter(f,i)
              p = logParameter(f,i)

              # For parameters not handled yet...

              if p==None:
                  body += '_code << "/* %s = ?? */";\n'%(i.name)

              # Special handling for input or output arrays

              elif p.startswith('boost::print::array'):
                  type = typeStrip(i.type)
                  size = i.size
                  if i.maxSize != None:
                    size = i.maxSize
                  if i.input:
                    if p.find('helper')==-1 and type!='GLchar' and type!='GLcharARB':
                      h2 += '    size_t _%sIndex = _context->codeInputNext++;\n'%(i.name)
                      h2 += '    _code << indent << \"const %s i\" << _%sIndex << \"[\" << %s << \"] = \" '%(type,i.name,expressionSimplify('(%s)'%size))
                      h2 += '<< array<%s,const char * const>(%s,%s,\"\",\"{ \",\" };\",\", \") '%(type,i.name,size)
                      h2 += '<< \"\\n\";\n'
                      body += '_code << \"i\" << _%sIndex;\n'%(i.name)
                    else:
                      body += '_code << "/* %s = ?? */";\n'%(i.name)
                  else:
                    if p.find('helper')==-1 and type!='GLchar' and type!='GLcharARB':
                      h2 += '    size_t _%sIndex = _context->codeOutputNext++;\n'%(i.name)
                      h2 += '    _code << indent << \"%s o\" << _%sIndex << \"[\" << %s << \"];\\n";\n'%(type,i.name,expressionSimplify('(%s)'%size))
                      body += '_code << \"o\" << _%sIndex;\n'%(i.name)
                    else:
                      body += '_code << "/* %s = ?? */";\n'%(i.name)

              # glTexImage2D etc

              elif i.input and i.size != None and (isinstance(i.size, str) or isinstance(i.size, unicode)) and i.size.startswith('helperGLPixelImageSize'):
                h2 += '    size_t _%sIndex = _context->codeTextureNext++;\n'%(i.name)
                h2 += '    _header << indent << \"const GLubyte texture\" << _%sIndex << \"[\" << helper::size::pixelImage(%s << \"] = \" '%(i.name,i.size.split('(',1)[1])
                h2 += '<< array<GLubyte,const char * const>(static_cast<const GLubyte *>(%s),helper::size::pixelImage(%s,\"\",\"{ \",\" }\",\",\") '%(i.name,i.size.split('(',1)[1])
                h2 += '<< \";\\n\";\n'
                h = True
                body += '_code << \"texture\" << _%sIndex;\n'%(i.name)

              elif p.startswith('boost::print::optional'):
                if i.cast != None:
                  body += '_code << reinterpret_cast<%s>(%s);\n'%(i.cast,i.name)
                else:
                  body += '_code << %s;\n'%(i.name)

              # 0x prefix for hex output

              elif p.startswith('boost::print::hex'):
                body += '_code << \"0x\" << %s;\n'%(p)

              elif p.startswith('boost::print::raw'):   # Buffer data needs better handling, revisit
                  body += '_code << "NULL";\n'

              else:
                  body += '_code << %s;\n'%(p)

              delim = True

            body += '    _code << ");%s\\n";\n'%(suffix)

        h1 += '    string_list< ::std::string > _code;\n'
        body += '    if (_context->codeSource)\n'
        body += '      fprintf(_context->codeSource,"%s",_code.str().c_str());\n'

        if h:
          h1 += '    string_list< ::std::string > _header;\n'
          body += '    if (_context->codeHeader)\n'
          body += '      fprintf(_context->codeHeader,"%s",_header.str().c_str());\n'

#         body += '    Internal("code_%s",_code);\n'%name

        code += h1 + h2 + body

        if not typeIsVoid(rType):
          code += '    return _ret;\n'
        code += '}\n\n'

    code += '\n'

  funcInit   = apiDispatchCodeInitCode( apis, args, 'code' )

  # Output

  substitute = {}
  substitute['LICENSE']         = args.license
  substitute['AUTOGENERATED']   = args.generated
  substitute['COPYRIGHT']       = args.copyright
  substitute['API_FUNC_DEFINE'] = code
  substitute['API_GLOBAL_DISPATCH_INIT'] = funcInit

  outputCode( '%s/RegalDispatchCode.cpp' % args.srcdir, dispatchCodeTemplate.substitute(substitute))
